Version 1: Sender-initiated Work-stealing The following alternative implementations of the acquire and communicate functions (along with additional definitions/state as shown) implement a sender-initiated work-stealing scheme. // extra definitions / state const task WAITING = -2 // code for ‘a task would be welcome ’ const task NOT_WAITING = -3 // code for ‘not receiving tasks ’ task s [P ]; // communication cells , all initially NOT_WAITING // called by workers when running out of work void acquire ( int i ) s[i ] = WAITING // ‘a task would be welcome ’ while (s [i] == WAITING ) // block until receiving a task noop add_task (i , s[i ]) s[i ] = NOT_WAITING // technically optional // consider pushing a task to an idle ( different ) worker void communicate ( int i) if ( empty (q[i ]) ) // cannot provide a task if we have none return int j = random in {0 , ... , P -1}\{ i} // pick a random other worker if s[ j] != WAITING // check if that worker is waiting for tasks return // give up if we picked a worker not waiting task t = peek_top (q[i ]) // attempt to atomically take the target communication slot bool r = compare_and_swap (& s[j] , WAITING , t) if (r ) // we successfully wrote the task ID to s[j] pop_top (q[i ]) // remove from our queue ; now worker j gets it The acquire function is only called when a worker has no tasks, and sets the worker’s communication cell to WAITING to signal that a task can be assigned to it. It then busy-waits until this cell’s value has been changed (to a task ID), and it then inserts this task and continues. The communicate function represents worker i considering sending a task to another worker. If it has a task to send, it randomly guesses (once) a different worker ID, and checks whether that worker is waiting for work (if not, it gives up for on communication for now). If so, it uses a compare-and-swap operation (which might be racing with other workers trying to assign the same worker a task) to attempt to atomically update the worker’s communication cell with the task ID to send, removing this from its own queue if this operation is successful. Tasks for version 1
(h) Prove that all functions provided for this scheme are memory-safe / crash
free  (as for task (c) above).